﻿/*
	Copyright © Bogdan Kobets 2011
	http://ElasticLogic.com
*/

using System.Collections.Generic;
using System.Linq;
using ElasticLogic.TagsMarker.Collections;
using ElasticLogic.TagsMarker.Concepts;
using ElasticLogic.TagsMarker.Extended;

namespace ElasticLogic.TagsMarker
{

	/// <summary>
	/// Singleton (different for each type E)
	/// </summary>
	/// <typeparam name="E">object (class)</typeparam>
	public static class Marker<E>
		where E : class
	{
		public static MarkerSettings Settings { get; private set; }

		private static Dictionary<Tag<E>, Category<E>> tagCategory;
		private static Dictionary<Tag<E>, List<E>> tagEntities;

		public static TagsCollection<E> Tags { get; private set; }

		public static CategoriesCollection<E> Categories { get; private set; }

		public static EntitiesCollection<E> Entities { get; private set; }

		static Marker()
		{
			Settings = new MarkerSettings(true, true, false);

			tagCategory = new Dictionary<Tag<E>, Category<E>>();
			tagEntities = new Dictionary<Tag<E>, List<E>>();

			Tags = new TagsCollection<E>();
			Categories = new CategoriesCollection<E>();
			Entities = new EntitiesCollection<E>();

			Tags.Creator = Tag<E>.Create;
			Categories.Creator = Category<E>.Create;
		}

		#region Tag - Category

		public static bool IncludeTagInCategory(Tag<E> tag, Category<E> category)
		{
			if (Tags.Contains(tag) && Categories.Contains(category))
			{
				tagCategory[tag] = category;
				return true;
			}
			else
				return false;
		}

		public static bool ExcludeTagFromCategory(Tag<E> tag)
		{
			return tagCategory.Remove(tag);
		}

		public static bool ClearCategoryTags(Category<E> category)
		{
			if (Categories.Contains(category))
			{
				List<Tag<E>> delete = new List<Tag<E>>();

				foreach (var pair in tagCategory)
				{
					if (pair.Value == category)
						delete.Add(pair.Key);
				}

				foreach (Tag<E> tag in delete)
					tagCategory.Remove(tag);

				return true;
			}
			else
				return false;
		}

		public static bool IsTagIncludedInAnyCategory(Tag<E> tag)
		{
			return tagCategory.Keys.Contains(tag);
		}

		public static bool IsTagIncludedInCategory(Tag<E> tag, Category<E> category)
		{
			if (tagCategory.Keys.Contains(tag))
				return (tagCategory[tag] == category);
			else
				return false;
		}

		public static Category<E> GetTagCategory(Tag<E> tag)
		{
			if (tagCategory.Keys.Contains(tag))
				return tagCategory[tag];
			else
				return null;
		}

		public static bool IsCategoryContainsTags(Category<E> category)
		{
			if (Categories.Contains(category))
			{
				foreach (Category<E> item in tagCategory.Values)
				{
					if (item == category)
						return true;
				}
			}
			return false;
		}

		public static List<Tag<E>> GetCategoryTags(Category<E> category)
		{
			List<Tag<E>> res = new List<Tag<E>>();

			if (Categories.Contains(category))
			{
				foreach (var pair in tagCategory)
				{
					if (pair.Value == category)
						res.Add(pair.Key);
				}
			}

			if (Settings.SortTags)
				res.Sort();

			return res;
		}

		public static List<Tag<E>> GetAssignmentTags(bool categorized)
		{
			List<Tag<E>> res;

			if (!categorized)
			{
				res = new List<Tag<E>>();

				foreach (Tag<E> tag in Tags)
				{
					if (!tagCategory.ContainsKey(tag))
						res.Add(tag);
				}
			}
			else
				res = tagCategory.Keys.ToList();

			if (Settings.SortTags)
				res.Sort();

			return res;
		}

		public static List<Category<E>> GetUsingCategories(bool filled)
		{
			List<Category<E>> res = new List<Category<E>>();

			foreach (Category<E> category in Categories)
			{
				bool include = tagCategory.ContainsValue(category);
				if (!filled)
					include = !include;

				if (include)
					res.Add(category);
			}

			if (Settings.SortCategories)
				res.Sort();

			return res;
		}

		#endregion Tag - Category

		#region Tag - Entity

		public static bool IncludeTagInEntity(Tag<E> tag, E entity)
		{
			if (Tags.Contains(tag) && Entities.Contains(entity))
			{
				if (!tagEntities[tag].Contains(entity))
				{
					tagEntities[tag].Add(entity);
					return true;
				}
			}
			return false;
		}

		public static bool ExcludeTagFromEntity(Tag<E> tag, E entity)
		{
			if (Tags.Contains(tag) && Entities.Contains(entity))
				return tagEntities[tag].Remove(entity);
			else
				return false;
		}

		public static bool ClearTagFromEntities(Tag<E> tag)
		{
			return tagEntities.Remove(tag);
		}

		public static bool ClearEntityTags(E entity)
		{
			if (Entities.Contains(entity))
			{
				foreach (var pair in tagEntities)
				{
					pair.Value.Remove(entity);
				}
				return true;
			}
			else
				return false;
		}

		public static bool IsEntityContainsAnyTag(E entity)
		{
			if (Entities.Contains(entity))
			{
				foreach (List<E> list in tagEntities.Values)
				{
					if (list.Contains(entity))
						return true;
				}
			}
			return false;
		}

		public static bool IsEntityContainsTag(E entity, Tag<E> tag)
		{
			if (Entities.Contains(entity) && Tags.Contains(tag))
			{
				if (tagEntities.Keys.Contains(tag))
				{
					return tagEntities[tag].Contains(entity);
				}
			}
			return false;
		}

		public static List<Tag<E>> GetEntityTags(E entity)
		{
			List<Tag<E>> res = new List<Tag<E>>();

			if (Entities.Contains(entity))
			{
				foreach (var pair in tagEntities)
				{
					if (pair.Value.Contains(entity))
						res.Add(pair.Key);
				}
			}

			if (Settings.SortTags)
				res.Sort();

			return res;
		}

		public static bool IsTagIncludedInEntities(Tag<E> tag)
		{
			return tagEntities.Keys.Contains(tag);
		}

		public static List<E> GetTagEntities(Tag<E> tag)
		{
			List<E> res = new List<E>();

			if (Tags.Contains(tag))
			{
				res.AddRange(tagEntities[tag]);
			}

			if (Settings.SortEntities)
				res.Sort();

			return res;
		}

		public static List<Tag<E>> GetUsingTags(bool used)
		{
			List<Tag<E>> res;

			if (!used)
			{
				res = new List<Tag<E>>();

				foreach (Tag<E> tag in Tags)
				{
					if (!tagEntities.Keys.Contains(tag))
						res.Add(tag);
				}
			}
			else
				res = tagEntities.Keys.ToList();

			if (Settings.SortTags)
				res.Sort();

			return res;
		}

		public static List<E> GetUsingEntities(bool used)
		{
			List<E> res = new List<E>();

			foreach (E entity in Entities)
			{
				bool include = false;
				foreach (List<E> list in tagEntities.Values)
				{
					if (list.Contains(entity))
					{
						include = true;
						break;
					}
				}

				if (!used)
					include = !include;

				if (include)
					res.Add(entity);
			}

			if (Settings.SortEntities)
				res.Sort();

			return res;
		}

		public static List<E> GetCategoryEntities(Category<E> category)
		{
			List<E> res = new List<E>();

			List<Tag<E>> tags = GetCategoryTags(category);

			foreach (Tag<E> tag in tags)
			{
				foreach (E entity in tagEntities[tag])
				{
					if (!res.Contains(entity))
						res.Add(entity);
				}
			}

			if (Settings.SortEntities)
				res.Sort();

			return res;
		}

		#endregion Tag - Entity

	}

}
